package service

import (
	"bytes"
	"context"
	"encoding/json"
	"fx_swift_forex/api"
	"fx_swift_forex/internal/model"
	"fx_swift_forex/pkg/code"
	"fx_swift_forex/pkg/global"
	"fx_swift_forex/pkg/internalerrors"
	"fx_swift_forex/pkg/page"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/jinzhu/copier"
	"github.com/spf13/cast"
	"mime/multipart"
	"strings"
)

// 受益人
type IBeneficiariesSrv interface {
	// 新增/更新/删除收益人
	OperatorBeneficiaries(ctx context.Context, req *api.OperatorBeneficiariesReq) error

	// 校验受益人银行详情
	ValidateBeneficiaries(ctx context.Context, req *api.ValidateBeneficiariesReq) (*api.ValidateBeneficiariesRes, error)

	// 获取受益人要求
	GetBeneficiaryRequirements(ctx context.Context, req *api.GetBeneficiaryRequirementsReq) (*api.GetBeneficiaryRequirementsRes, error)

	// 通过受益人ID查询信息
	GetBeneficiaryIdToInfo(ctx context.Context, beneficiaryId string) (*api.GetBeneficiaryIdToInfoRes, error)

	// 受益人列表
	BeneficiaryList(ctx context.Context, req *api.BeneficiaryListReq) (*page.Collection[*api.BeneficiaryListRes], error)

	// 修改受益人昵称
	EditBeneficiaryNickName(ctx context.Context, req *api.EditBeneficiaryNickNameReq) error
}

type beneficiariesSrv struct {
}

func NewBeneficiariesSrv() IBeneficiariesSrv {
	return &beneficiariesSrv{}
}

// 新增/更新/删除收益人
func (b *beneficiariesSrv) OperatorBeneficiaries(ctx context.Context, req *api.OperatorBeneficiariesReq) error {
	var (
		createUrl     = global.ViperObj.GetString("forex.baseUrl") + "/v2/beneficiaries/create"
		updateUrl     = global.ViperObj.GetString("forex.baseUrl") + "/v2/beneficiaries/"
		deleteUrl     = global.ViperObj.GetString("forex.baseUrl") + "/v2/beneficiaries/"
		deleteHalfUrl = "/delete"
		urls          string
		err           error
		body          []byte
		obj           *api.OperatorBeneficiariesRes
		payload       *bytes.Buffer
		writer        *multipart.Writer
		account       string
		customerId    string
	)
	//redis获取customerId
	account = ctx.Value("Account").(string)
	customerId, err = GetRedisCustomerId(ctx)
	if err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}

	reqObj := &api.CreateBeneficiariesReq{}
	if err = copier.Copy(reqObj, req); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}
	payload, writer, err = operatorData(reqObj)
	if err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}
	if req.Method == "add" {
		urls = createUrl
		body, err = PostRequestToCC(ctx, urls, writer.FormDataContentType(), payload)
		if err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}
		if err = json.Unmarshal(body, &obj); err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}
		if _, err = global.GFDB.Ctx(ctx).Model("forex_beneficiaries").Insert(&model.ForexBeneficiaries{
			Account:                        account,
			BeneficiaryId:                  obj.Id,
			CustomerId:                     customerId,
			Name:                           obj.Name,
			BankAccountHolderName:          obj.BankAccountHolderName,
			BankCountry:                    obj.BankCountry,
			Currency:                       obj.Currency,
			Email:                          obj.Email,
			BeneficiaryAddress:             strings.Join(obj.BeneficiaryAddress, ","),
			BeneficiaryCountry:             obj.BeneficiaryCountry,
			AccountNumber:                  obj.AccountNumber,
			RoutingCodeType1:               obj.RoutingCodeType1,
			RoutingCodeValue1:              obj.RoutingCodeValue1,
			RoutingCodeType2:               obj.RoutingCodeType2,
			RoutingCodeValue2:              obj.RoutingCodeValue2,
			BicSwift:                       obj.BicSwift,
			Iban:                           obj.Iban,
			DefaultBeneficiary:             obj.DefaultBeneficiary,
			BankAddress:                    strings.Join(obj.BankAddress, ","),
			BankName:                       obj.BankName,
			BankAccountType:                obj.BankAccountType,
			BeneficiaryEntityType:          obj.BeneficiaryEntityType,
			BeneficiaryCompanyName:         obj.BeneficiaryCompanyName,
			BeneficiaryFirstName:           obj.BeneficiaryFirstName,
			BeneficiaryLastName:            obj.BeneficiaryLastName,
			BeneficiaryCity:                obj.BeneficiaryCity,
			BeneficiaryPostcode:            obj.BeneficiaryPostcode,
			BeneficiaryStateOrProvince:     obj.BeneficiaryStateOrProvince,
			BeneficiaryDateOfBirth:         obj.BeneficiaryDateOfBirth,
			BeneficiaryIdentificationType:  obj.BeneficiaryIdentificationType,
			BeneficiaryIdentificationValue: obj.BeneficiaryIdentificationValue,
			PaymentTypes:                   strings.Join(obj.PaymentTypes, ","),
			BeneficiaryExternalReference:   obj.BeneficiaryExternalReference,
			BusinessNature:                 obj.BusinessNature,
			CompanyWebsite:                 obj.CompanyWebsite,
			SignType:                       req.SignType,
		}); err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}

	} else if req.Method == "update" {
		urls = updateUrl + req.BeneficiaryId
		body, err = PostRequestToCC(ctx, urls, writer.FormDataContentType(), payload)
		if err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}
		if err = json.Unmarshal(body, &obj); err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}
		if _, err = global.GFDB.Model("forex_beneficiaries").Data(g.Map{
			"name":                             req.Name,
			"bank_account_holder_name":         req.BankAccountHolderName,
			"bank_country":                     req.BankCountry,
			"Currency":                         req.Currency,
			"Email":                            req.Email,
			"beneficiary_address":              req.BeneficiaryAddress,
			"beneficiary_country":              req.BeneficiaryCountry,
			"account_number":                   req.AccountNumber,
			"routing_code_type_1":              req.RoutingCodeType1,
			"routing_code_value_1":             req.RoutingCodeValue1,
			"routing_code_type_2":              req.RoutingCodeType2,
			"routing_code_value_2":             req.RoutingCodeValue2,
			"bic_swift":                        req.BicSwift,
			"iban":                             req.Iban,
			"default_beneficiary":              req.DefaultBeneficiary,
			"bank_address":                     req.BankAddress,
			"bank_name":                        req.BankName,
			"bank_account_type":                req.BankAccountType,
			"beneficiary_entity_type":          req.BeneficiaryEntityType,
			"beneficiary_company_name":         req.BeneficiaryCompanyName,
			"beneficiary_first_name":           req.BeneficiaryFirstName,
			"beneficiary_last_name":            req.BeneficiaryLastName,
			"beneficiary_city":                 req.BeneficiaryCity,
			"beneficiary_postcode":             req.BeneficiaryPostcode,
			"beneficiary_state_or_province":    req.BeneficiaryStateOrProvince,
			"beneficiary_date_of_birth":        req.BeneficiaryDateOfBirth,
			"beneficiary_identification_type":  req.BeneficiaryIdentificationType,
			"beneficiary_identification_value": req.BeneficiaryIdentificationValue,
			"payment_types":                    strings.Join(req.PaymentTypes, ","),
			"on_behalf_of":                     req.OnBehalfOf,
			"beneficiary_external_reference":   req.BeneficiaryExternalReference,
			"business_nature":                  req.BusinessNature,
			"company_website":                  req.CompanyWebsite,
		}).Where("beneficiary_id=?", req.BeneficiaryId).Update(); err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}
	} else if req.Method == "del" {
		urls = deleteUrl + req.BeneficiaryId + deleteHalfUrl
		payload = &bytes.Buffer{}
		writer = multipart.NewWriter(payload)
		body, err = PostRequestToCC(ctx, urls, writer.FormDataContentType(), payload)
		if err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}
		if err = json.Unmarshal(body, &obj); err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}
		if _, err = global.GFDB.Model("forex_beneficiaries").Delete("beneficiary_id=?", req.BeneficiaryId); err != nil {
			return internalerrors.New(code.ErrUnknown, err.Error())
		}
	}
	return err
}

// 校验受益人银行详情
func (b *beneficiariesSrv) ValidateBeneficiaries(ctx context.Context, req *api.ValidateBeneficiariesReq) (*api.ValidateBeneficiariesRes, error) {
	var (
		urls    = global.ViperObj.GetString("forex.baseUrl") + "/v2/beneficiaries/validate"
		err     error
		body    []byte
		payload *bytes.Buffer
		writer  *multipart.Writer
		obj     *api.ValidateBeneficiariesRes
	)
	payload, writer, err = validateOperatorData(req)
	if err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	body, err = PostRequestToCC(ctx, urls, writer.FormDataContentType(), payload)
	if err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	if err = json.Unmarshal(body, &obj); err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	return obj, err
}

// 获取受益人要求
func (b *beneficiariesSrv) GetBeneficiaryRequirements(ctx context.Context, req *api.GetBeneficiaryRequirementsReq) (*api.GetBeneficiaryRequirementsRes, error) {
	var (
		urls   = global.ViperObj.GetString("forex.baseUrl") + "/v2/reference/beneficiary_required_details"
		err    error
		body   []byte
		obj    *api.GetBeneficiaryRequirementsRes
		params = make(map[string]string)
	)
	params["currency"] = req.Currency
	params["bank_account_country"] = req.BankAccountCountry
	params["beneficiary_country"] = req.BeneficiaryCountry
	body, err = GetRequestToCC(ctx, urls, params)
	if err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	if err = json.Unmarshal(body, &obj); err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	return obj, nil
}

// 通过受益人ID查询信息
func (b *beneficiariesSrv) GetBeneficiaryIdToInfo(ctx context.Context, beneficiaryId string) (*api.GetBeneficiaryIdToInfoRes, error) {
	var (
		err            error
		beneficiaryObj *api.GetBeneficiaryIdToInfoRes
	)
	if err = global.GFDB.Ctx(ctx).Model("forex_beneficiaries").
		Where("beneficiary_id=?", beneficiaryId).Scan(&beneficiaryObj); err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	if beneficiaryObj == nil {
		return nil, internalerrors.New(code.ErrUnknown, "受益人ID有误")
	}
	return beneficiaryObj, err
}

// 受益人列表
func (b *beneficiariesSrv) BeneficiaryList(ctx context.Context, req *api.BeneficiaryListReq) (*page.Collection[*api.BeneficiaryListRes], error) {
	var (
		err        error
		total      int
		customerId string
		resultList []*api.BeneficiaryListRes
	)

	collectionDto := &page.Collection[*api.BeneficiaryListRes]{}
	collectionDto.Page = uint64(req.PageNo)
	collectionDto.PageSize = uint64(req.PageSize)

	customerId, err = GetRedisCustomerId(ctx)
	if err != nil {
		return nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	if len(customerId) > 0 {
		mod := global.GFDB.Ctx(ctx).Model("forex_beneficiaries").
			Fields("beneficiary_id,name,currency,iban,bic_swift,sign_type")
		if len(req.Name) > 0 {
			mod.WhereLike("name", "%"+req.Name+"%")
		}
		if len(req.Currency) > 0 {
			mod.WhereLike("currency", "%"+req.Currency+"%")
		}
		//if len(req.StartTime) > 0 && len(req.EndTime) > 0 {
		//	mod.Where("itr.created_at >=? and itr.created_at < ?", req.StartTime+" 00:00:00", req.EndTime+" 23:59:59")
		//}
		mod.Where("customer_id=?", customerId)
		if len(req.SignType) == 0 {
			mod.Where("sign_type='my' or sign_type='other'")
		} else {
			mod.Where("sign_type=?", req.SignType)
		}
		mod.Order("created_at desc")
		mod.Limit(int(page.PageToOffset(uint64(req.PageNo), uint64(req.PageSize))), cast.ToInt(req.PageSize))
		err = mod.ScanAndCount(&resultList, &total, false)
		collectionDto.TotalPage = page.GetTotalPage(uint64(req.PageNo), uint64(req.PageSize), cast.ToInt64(total))
		if err != nil {
			return nil, internalerrors.New(code.ErrUnknown, "数据查询异常")
		}
		collectionDto.Total = cast.ToInt64(total)
		collectionDto.List = resultList
	}
	return collectionDto, nil
}

// 修改受益人昵称
func (b *beneficiariesSrv) EditBeneficiaryNickName(ctx context.Context, req *api.EditBeneficiaryNickNameReq) error {
	var (
		err error
	)
	if _, err = global.GFDB.Ctx(ctx).Model("forex_beneficiaries").Data(g.Map{
		"nick_name": req.NickName,
	}).Where("beneficiary_id=?", req.BeneficiaryId).Update(); err != nil {
		return internalerrors.New(code.ErrUnknown, err.Error())
	}
	return err
}

func operatorData(reqObj *api.CreateBeneficiariesReq) (*bytes.Buffer, *multipart.Writer, error) {
	var (
		err error
	)
	payload := &bytes.Buffer{}
	writer := multipart.NewWriter(payload)
	if len(reqObj.Name) > 0 {
		_ = writer.WriteField("name", reqObj.Name)
	}
	if len(reqObj.BankAccountHolderName) > 0 {
		_ = writer.WriteField("bank_account_holder_name", reqObj.BankAccountHolderName)
	}
	if len(reqObj.BankCountry) > 0 {
		_ = writer.WriteField("bank_country", reqObj.BankCountry)
	}
	if len(reqObj.Currency) > 0 {
		_ = writer.WriteField("currency", reqObj.Currency)
	}
	if len(reqObj.Email) > 0 {
		_ = writer.WriteField("email", reqObj.Email)
	}
	if len(reqObj.BeneficiaryAddress) > 0 {
		_ = writer.WriteField("beneficiary_address", reqObj.BeneficiaryAddress)
	}
	if len(reqObj.BeneficiaryCountry) > 0 {
		_ = writer.WriteField("beneficiary_country", reqObj.BeneficiaryCountry)
	}
	if len(reqObj.AccountNumber) > 0 {
		_ = writer.WriteField("account_number", reqObj.AccountNumber)
	}
	if len(reqObj.RoutingCodeType1) > 0 {
		_ = writer.WriteField("routing_code_type_1", reqObj.RoutingCodeType1)
	}
	if len(reqObj.RoutingCodeValue1) > 0 {
		_ = writer.WriteField("routing_code_value_1", reqObj.RoutingCodeValue1)
	}
	if len(reqObj.RoutingCodeType2) > 0 {
		_ = writer.WriteField("routing_code_type_2", reqObj.RoutingCodeType2)
	}
	if len(reqObj.RoutingCodeValue2) > 0 {
		_ = writer.WriteField("routing_code_value_2", reqObj.RoutingCodeValue2)
	}
	if len(reqObj.BicSwift) > 0 {
		_ = writer.WriteField("bic_swift", reqObj.BicSwift)
	}
	if len(reqObj.Iban) > 0 {
		_ = writer.WriteField("iban", reqObj.Iban)
	}
	if len(reqObj.AccountNumber) > 0 {
		_ = writer.WriteField("default_beneficiary", cast.ToString(reqObj.DefaultBeneficiary))
	}
	if len(reqObj.BankAddress) > 0 {
		_ = writer.WriteField("bank_address", reqObj.BankAddress)
	}
	if len(reqObj.BankName) > 0 {
		_ = writer.WriteField("bank_name", reqObj.BankName)
	}
	if len(reqObj.BankAccountType) > 0 {
		_ = writer.WriteField("bank_account_type", reqObj.BankAccountType)
	}
	if len(reqObj.BeneficiaryEntityType) > 0 {
		_ = writer.WriteField("beneficiary_entity_type", reqObj.BeneficiaryEntityType)
	}
	if len(reqObj.BeneficiaryCompanyName) > 0 {
		_ = writer.WriteField("beneficiary_company_name", reqObj.BeneficiaryCompanyName)
	}
	if len(reqObj.BeneficiaryFirstName) > 0 {
		_ = writer.WriteField("beneficiary_first_name", reqObj.BeneficiaryFirstName)
	}
	if len(reqObj.BeneficiaryLastName) > 0 {
		_ = writer.WriteField("beneficiary_last_name", reqObj.BeneficiaryLastName)
	}
	if len(reqObj.BeneficiaryCity) > 0 {
		_ = writer.WriteField("beneficiary_city", reqObj.BeneficiaryCity)
	}
	if len(reqObj.BeneficiaryPostcode) > 0 {
		_ = writer.WriteField("beneficiary_postcode", reqObj.BeneficiaryPostcode)
	}
	if len(reqObj.BeneficiaryStateOrProvince) > 0 {
		_ = writer.WriteField("beneficiary_state_or_province", reqObj.BeneficiaryStateOrProvince)
	}
	if len(reqObj.BeneficiaryDateOfBirth) > 0 {
		_ = writer.WriteField("beneficiary_date_of_birth", reqObj.BeneficiaryDateOfBirth)
	}
	if len(reqObj.BeneficiaryIdentificationType) > 0 {
		_ = writer.WriteField("beneficiary_identification_type", reqObj.BeneficiaryIdentificationType)
	}
	if len(reqObj.BeneficiaryIdentificationValue) > 0 {
		_ = writer.WriteField("beneficiary_identification_value", reqObj.BeneficiaryIdentificationValue)
	}
	if len(reqObj.PaymentTypes) > 0 {
		_ = writer.WriteField("payment_types", strings.Join(reqObj.PaymentTypes, ","))
	}
	if len(reqObj.OnBehalfOf) > 0 {
		_ = writer.WriteField("on_behalf_of", reqObj.OnBehalfOf)
	}
	if len(reqObj.BeneficiaryExternalReference) > 0 {
		_ = writer.WriteField("beneficiary_external_reference", reqObj.BeneficiaryExternalReference)
	}
	if len(reqObj.BusinessNature) > 0 {
		_ = writer.WriteField("business_nature", reqObj.BusinessNature)
	}
	if len(reqObj.CompanyWebsite) > 0 {
		_ = writer.WriteField("company_website", reqObj.CompanyWebsite)
	}
	if err = writer.Close(); err != nil {
		return nil, nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	return payload, writer, err
}

func validateOperatorData(req *api.ValidateBeneficiariesReq) (*bytes.Buffer, *multipart.Writer, error) {
	var (
		err error
	)
	payload := &bytes.Buffer{}
	writer := multipart.NewWriter(payload)
	_ = writer.WriteField("bank_country", req.BankCountry)
	_ = writer.WriteField("currency", req.Currency)
	if len(req.BeneficiaryAddress) > 0 {
		_ = writer.WriteField("beneficiary_address", req.BeneficiaryAddress)
	}
	if len(req.BeneficiaryCountry) > 0 {
		_ = writer.WriteField("beneficiary_country", req.BeneficiaryCountry)
	}
	if len(req.AccountNumber) > 0 {
		_ = writer.WriteField("account_number", req.AccountNumber)
	}
	if len(req.BeneficiaryFirstName) > 0 {
		_ = writer.WriteField("beneficiary_first_name", req.BeneficiaryFirstName)
	}
	if len(req.BeneficiaryLastName) > 0 {
		_ = writer.WriteField("beneficiary_last_name", req.BeneficiaryLastName)
	}
	if len(req.BeneficiaryCity) > 0 {
		_ = writer.WriteField("beneficiary_city", req.BeneficiaryCity)
	}
	if len(req.RoutingCodeType1) > 0 {
		_ = writer.WriteField("routing_code_type_1", req.RoutingCodeType1)
	}
	if len(req.RoutingCodeValue1) > 0 {
		_ = writer.WriteField("routing_code_value_1", req.RoutingCodeValue1)
	}
	if len(req.RoutingCodeType2) > 0 {
		_ = writer.WriteField("routing_code_type_2", req.RoutingCodeType2)
	}
	if len(req.RoutingCodeValue2) > 0 {
		_ = writer.WriteField("routing_code_value_2", req.RoutingCodeValue2)
	}
	if len(req.BicSwift) > 0 {
		_ = writer.WriteField("bic_swift", req.BicSwift)
	}
	if len(req.Iban) > 0 {
		_ = writer.WriteField("iban", req.Iban)
	}
	if len(req.BankAddress) > 0 {
		_ = writer.WriteField("bank_address", req.BankAddress)
	}
	if len(req.BankName) > 0 {
		_ = writer.WriteField("bank_name", req.BankName)
	}
	if len(req.BankAccountType) > 0 {
		_ = writer.WriteField("bank_account_type", req.BankAccountType)
	}
	if len(req.BeneficiaryEntityType) > 0 {
		_ = writer.WriteField("beneficiary_entity_type", req.BeneficiaryEntityType)
	}
	if len(req.BeneficiaryCompanyName) > 0 {
		_ = writer.WriteField("beneficiary_company_name", req.BeneficiaryCompanyName)
	}
	if len(req.BeneficiaryFirstName) > 0 {
		_ = writer.WriteField("beneficiary_first_name", req.BeneficiaryFirstName)
	}
	if len(req.BeneficiaryLastName) > 0 {
		_ = writer.WriteField("beneficiary_last_name", req.BeneficiaryLastName)
	}
	if len(req.BeneficiaryCity) > 0 {
		_ = writer.WriteField("beneficiary_city", req.BeneficiaryCity)
	}
	if len(req.BeneficiaryPostcode) > 0 {
		_ = writer.WriteField("beneficiary_postcode", req.BeneficiaryPostcode)
	}
	if len(req.BeneficiaryStateOrProvince) > 0 {
		_ = writer.WriteField("beneficiary_state_or_province", req.BeneficiaryStateOrProvince)
	}
	if len(req.BeneficiaryDateOfBirth) > 0 {
		_ = writer.WriteField("beneficiary_date_of_birth", req.BeneficiaryDateOfBirth)
	}
	if len(req.BeneficiaryIdentificationType) > 0 {
		_ = writer.WriteField("beneficiary_identification_type", req.BeneficiaryIdentificationType)
	}
	if len(req.BeneficiaryIdentificationValue) > 0 {
		_ = writer.WriteField("beneficiary_identification_value", req.BeneficiaryIdentificationValue)
	}
	if err = writer.Close(); err != nil {
		return nil, nil, internalerrors.New(code.ErrUnknown, err.Error())
	}
	return payload, writer, err
}
